#pragma once
#include "requestor.hpp"
#include <functional>
#include <unordered_map>


namespace rpc
{
    namespace client
    {
        class TopicManager
        {
        public:
            using ptr = std::shared_ptr<TopicManager>;
            using SubCallback = std::function<void(const std::string &key, const std::string & msg)>;
            TopicManager(const Requestor::ptr &requestor):_requestor(requestor)
            {}
            bool create(const BaseConnection::ptr &conn, const std::string &key)
            {
                return commonRequest(conn, key, TopicOptype::TOPIC_CREATE);
            }
            bool remove(const BaseConnection::ptr &conn, const std::string &key)
            {
                return commonRequest(conn, key, TopicOptype::TOPIC_REMOVE);  
            }
            bool subscribe(const BaseConnection::ptr &conn, const std::string &key, const SubCallback &cb)
            {
                addSubscribe(key, cb);
                bool ret = commonRequest(conn, key, TopicOptype::TOPIC_SUBSCRIBE);  
                if(!ret)
                {
                    delSubscribe(key);
                    return false;
                }
                return true;
            }
            bool cancel(const BaseConnection::ptr &conn, const std::string &key)
            {
                delSubscribe(key);
                return commonRequest(conn, key, TopicOptype::TOPIC_CANCEL);  
            }
            bool publish(const BaseConnection::ptr &conn, const std::string &key, const std::string &msg)
            {
                return commonRequest(conn, key, TopicOptype::TOPIC_PUBLISH, msg);  

            }
            void onPublish(const BaseConnection::ptr &conn, const TopicRequest::ptr &msg)
            {
                //进行类型判断
                auto otype = msg->optype();
                if(otype != TopicOptype::TOPIC_PUBLISH)
                {
                    ELOG("收到了错误的消息类型");
                    return;
                }
                //取出消息主题和内容
                std::string topic_key = msg->topicKey();
                std::string topic_msg = msg->topicMsg();
                //通过取出的内容，查找主题处理回调，有则处理，无则报错
                auto cb = getSubscribe(topic_key);
                if(!cb)
                {
                    ELOG("收到了 %s 主题消息，但无处理回调！", topic_key.c_str());
                    return;
                }
                return cb(topic_key, topic_msg);
            }
        private:
            void addSubscribe(const std::string &key, const SubCallback &cb)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _topic_callbacks.insert({key, cb});
            }
            void delSubscribe(const std::string &key)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                _topic_callbacks.erase(key);
            }
            const SubCallback getSubscribe(const std::string &key)
            {
                std::unique_lock<std::mutex> lock(_mutex);
                auto sub_it = _topic_callbacks.find(key);
                if(sub_it == _topic_callbacks.end())
                {
                    return SubCallback();
                }
                return sub_it->second;
            }
            bool commonRequest(const BaseConnection::ptr &conn, const std::string &key, 
                    TopicOptype type, const std::string &msg = "")
                {
                    //构建请求对象，填充数据
                    auto msg_req = MessageFactory::create<TopicRequest>();
                    msg_req->setId(UUID::uuid());
                    msg_req->setMtype(MType::REQ_TOPIC);
                    msg_req->setOtype(type);
                    msg_req->setTopicKey(key);
                    if(type == TopicOptype::TOPIC_PUBLISH)
                    {
                        msg_req->setTopicMsg(msg);
                    }
                    //向服务端发送请求，等待响应
                    BaseMessage::ptr msg_rsp;
                    bool ret = _requestor->send(conn, msg_req, msg_rsp);
                    if(!ret)
                    {
                        ELOG("主题创建请求失败");
                        return false;
                    }
                    //判断请求处理是否成功
                    auto topic_rsp_msg  = std::dynamic_pointer_cast<TopicResponse>(msg_rsp);
                    if(!topic_rsp_msg)
                    {
                        ELOG("topic创建响应，向下类型转换失败");
                        return false;
                    }
                    if(topic_rsp_msg->rcode() != RCode::RCODE_OK)
                    {
                        std::string err_msg = errReason(topic_rsp_msg->rcode());
                        ELOG("topic创建请求出错: %s", err_msg.c_str());
                        return false;
                    }
                    return true;
                }
        private:
            std::mutex _mutex;
            std::unordered_map<std::string, SubCallback> _topic_callbacks;
            Requestor::ptr _requestor;
        };
    }
}